<?php

/*
 *	Trap namespace
 */
namespace Nano\Trap
{
	/*
	 *	Trap PHP notices and warnings
	 *
	 *	@extends	\Exception
	 */
	class Errors extends \Exception
	{
		/*
		 *	$template	string	Styled debug line
		 *
		 *	@protected
		 */
		protected $template = "<pre><strong>%s</strong>: %s in %s(%d);</pre>\n";
		
		/*
		 *	$string	string	Simple debug line
		 *
		 *	@protected
		 */
		protected $string = "%s: %s in %s(%d);\n";
		
		/*
		 *	$formating	boolean	Use styling?
		 *
		 *	@protected
		 */
		protected $formating = true;
		
		/*
		 *	$debug	boolean	Show sexy error information?
		 *
		 *	@protected
		 */
		protected $debug = true;
		
		/*
		 *	$highlight	boolean	Highlight source?
		 *
		 *	@info	To buggy
		 *	@info	I do not recommend to use it
		 *	@protected
		 */
		protected $highlight = false;
		
		/*
		 *	$highlighto	array	Highlight options
		 */
		protected $highlighto = array
		(
			'error.color' => '#FFBABA',
			'notice.color' => '#FFF5BA',
		);
		
		/*
		 *	$errors	array	List of PHP errors
		 *
		 *	@protected
		 */
		protected $errors = array
		(
			1 => array('E_ERROR', 'Fatal Run-time Error', true),
			2 => array('E_WARNING', 'Run-time Warning', true),
			4 => array('E_PARSE', 'Compile-time Parse Error', true),
			8 => array('E_NOTICE', 'Run-time Notice', false),
			16 => array('E_CORE_ERROR', 'Initial Startup Fatal Error', true),
			32 => array('E_CORE_WARNING', 'Initial Startup Warnig', true),
			64 => array('E_COMPILE_ERROR', 'Fatal Compile-time Error', true),
			128 => array('E_COMPILE_WARNING', 'Compile-time Warning', true),
			256 => array('E_USER_ERROR', 'User-generated Error', true),
			512 => array('E_USER_WARNING', 'User-generated Warning', true),
			1024 => array('E_USER_NOTICE', 'User-generated Notice', true),
			2048 => array('E_STRICT', 'Strict', false),
			4096 => array('E_RECOVERABLE_ERROR', 'Catchable Fatal Error', true),
			8192 => array('E_DEPRECATED', 'Run-time Notice', false),
			16384 => array('E_USER_DEPRECATED', 'Run-time User-generated Notice', false),
			30719 => array('E_ALL', 'All', false)
		);
		
		/*
		 *	$message	string	Error message
		 *
		 *	@protected
		 */
		protected $message;
		
		/*
		 *	$level	int	Error level
		 *
		 *	@protected
		 */
		protected $level;
		
		/*
		 *	$file	string	Error file
		 *
		 *	@protected
		 */
		protected $file;
		
		/*
		 *	$line	int	Error line
		 *
		 *	@protected
		 */
		protected $line;
		
		/*
		 *	Constructor
		 */
		public function __construct($message, $level = 0, $file = null, $line = 0)
		{
			parent::__construct($message, $level);
			
			$this->message = (string)$message;
			$this->level = (int)$level;
			$this->file = (string)$file;
			$this->line = (int)$line;
			
			print(sprintf(($this->formating ? $this->template : $this->string), $this->get_error_name($this->level), $this->message, $this->file, $this->line));
			
			if($this->debug)
			{
				print($this->get_error_debug());
			}
			
			if($this->get_error_level())
			{
				exit();
			}
		}
		
		/*
		 *	Get information about errors
		 *
		 *	@return	string	Generated HTML
		 *	@return	false	On failure
		 *	@info	I'm using <code> tag instead of <pre>, cuz it's inline tag.
		 *	@fixme	If you do not use the highlight, the number of the line shifts
		 *	@todo	Link with view or template
		 */
		public function get_error_debug()
		{
			if(isset($this->file) && isset($this->line))
			{
				if(is_file($this->file))
				{
					if($this->highlight)
					{
						$contents = highlight_file($this->file, true);
						
						$contents = str_replace("<br />", "\n", $contents);
					}
					else
					{
						$contents = file_get_contents($this->file);
					}
					
					$contents = explode("\n", $contents);
					
					$result = "<pre>\n";
					
					$b = 0;
					
					foreach($contents as $num => $code)
					{
						$line = $this->highlight ? $num : $num + 1;
						
						if(($line > $this->line - 10) && ($line != $this->line) && !($line > $this->line))
						{
							$b++;
							
							$result .= '<label style="opacity: 0.'.$b.';">'.$line.'. '.$code.'</label>'."\n";
						}
						elseif($line == $this->line)
						{
							$result .= '<label style="background: '.($this->get_error_name() == false ? $this->highlighto['notice.color'] : $this->highlighto['error.color']).';">'.$line.'. '.$code.'</label>'."\n";
						}
					}
					
					$result .= "</pre>\n";
					
					return $result;
				}
			}
			
			return false;
		}
		
		/*
		 *	Get error level by it's code
		 *
		 *	@param	int
		 *	@return	boolean
		 */
		public function get_error_level($id = null)
		{
			if(!isset($id))
			{
				$id = $this->level;
			}
			
			if(array_key_exists($id, $this->errors) && $this->errors[$id][2] === true)
			{
				return true;
			}
			
			return false;
		}
		
		/*
		 *	Get error name by it's code
		 *
		 *	@param	int
		 *	@return	string	On success
		 *	@return	null	If key does not exist
		 */
		public function get_error_name($id = null)
		{
			if(!isset($id))
			{
				$id = $this->level;
			}
			
			if(array_key_exists($id, $this->errors))
			{
				return $this->errors[$id][1];
			}
			
			return null;
		}
	}
	
	/*
	 *	Trap exceptions
	 *
	 *	@extends	Errors
	 */
	class Exceptions extends Errors
	{
		public function __construct($exception)
		{
			if(!$exception)
			{
				return false;
			}
			
			$this->exception = $exception;
			
			$this->file = $this->exception->file;
			$this->line = $this->exception->line;
			$this->level = 4096;
			$this->message = $this->exception->message;
			
			parent::__construct($this->message, $this->level, $this->file, $this->line);
		}
	}
	
	/*
	 *	Trap shutdowns
	 *
	 *	@extends	Errors
	 */
	class Shutdowns extends Errors
	{
		/*
		 *	Constructor
		 */
		public function __construct()
		{
			$this->error = error_get_last();
			
			if(!$this->error)
			{
				return false;
			}
			
			$this->file = $this->error['file'];
			$this->line = $this->error['line'];
			$this->level = $this->error['type'];
			$this->message = $this->error['message'];
			
			parent::__construct($this->message, $this->level, $this->file, $this->line);
		}
	}
}